/// @file Wall.cpp
/// @brief manages the creation and manipulation of the world boundaries

#include "ngl/NGLInit.h"

#include "Wall.h"


Wall::Wall
        (
            const float _radius,
            const bool _enableWall,
            const bool _hardWall,
            const float _weight
        )
{
    //init bounds
    initialiseBounds(_radius);

    //init params
    m_enableWall = _enableWall;
    m_hardWall = _hardWall;
    m_softLimitWeight = _weight;
}

void Wall::initialiseBounds(const float _radius)
{
    //x, y and z limits are the same = radius
    initialiseBounds(_radius, _radius, _radius);
}

void Wall::initialiseBounds
                    (
                        const float _xLimits,
                        const float _yLimits,
                        const float _zLimits
                    )
{
    //define wall limits
    xMin = -_xLimits; xMax = _xLimits;
    yMin = -_yLimits; yMax = _yLimits;
    zMin = -_zLimits; zMax = _zLimits;
}

void Wall::loadGeometry()
{
    //create vbo for wall
}

void Wall::draw
            (
                ngl::TransformStack _txStack,
                ShaderManager* io_sman,
                const std::string _shader
            )
{
    ngl::VBOPrimitives* prim=ngl::VBOPrimitives::instance();

    //draw the cubicle wall if enabled
    if (m_enableWall)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        _txStack.pushTransform();
        {
            _txStack.setPosition(0, 0, 0);

            _txStack.setScale(xMax - xMin, yMax - yMin, zMax - zMin);

            //update transform and color to shader
            io_sman->updateModelColor(_shader, _txStack.getCurrAndGlobal().getMatrix(), ngl::Colour(1, 1, 0));

            //draw primitive
            prim->draw("cube");

            //io_sman->m_shaders->use(_shader, false);
        }
        _txStack.popTransform();
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    }

}

bool Wall::isWithinBounds
                    (
                        const Boid* _boid,
                        ngl::Vector &o_deltaPosition
                    ) const
{
    //assume boid position is within bounds
    bool isWithinBounds = true;

    //if wall is enabled, do the limits check
    if (m_enableWall)
    {
        //check boid against all walls
        //if boid is outside, calculate change in position required to bring boid back
        if (_boid->getPosition().m_x < xMin)
        {
            isWithinBounds = false;
            o_deltaPosition.m_x = _boid->getPosition().m_x - xMin;
        }
        if (_boid->getPosition().m_x > xMax)
        {
            isWithinBounds = false;
            o_deltaPosition.m_x = _boid->getPosition().m_x - xMax;
        }
        if (_boid->getPosition().m_y < yMin)
        {
            isWithinBounds = false;
            o_deltaPosition.m_y = _boid->getPosition().m_y - yMin;
        }
        if (_boid->getPosition().m_y > yMax)
        {
            isWithinBounds = false;
            o_deltaPosition.m_y = _boid->getPosition().m_y - yMax;
        }

        if (_boid->getPosition().m_z < zMin)
        {
            isWithinBounds = false;
            o_deltaPosition.m_z = _boid->getPosition().m_z - zMin;
        }
        if (_boid->getPosition().m_z > zMax)
        {
            isWithinBounds = false;
            o_deltaPosition.m_z = _boid->getPosition().m_z - zMax;
        }
    }

    return isWithinBounds;
}

void Wall::setBounds(const ngl::Vector _bound)
{
    //initialise walls with bounds limits
    initialiseBounds(_bound.m_x / 2, _bound.m_y / 2, _bound.m_z / 2);
}

ngl::Vector Wall::getBounds() const
{
    //get the max bound capacity in each direction and return as vector
    return ngl::Vector(xMax - xMin, yMax - yMin, zMax - zMin);
}
